package com.splic.product.service.impl;

import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.splic.common.core.DeviceContext;
import com.splic.common.exception.BusinessException;
import com.splic.common.utils.DateUtils;
import com.splic.common.utils.StringUtils;
import com.splic.product.domain.*;
import com.splic.product.dto.LocationInfoDTO;
import com.splic.product.mapper.AccuAreaMapper;
import com.splic.product.mapper.WeatherMapper;
import com.splic.product.service.*;
import com.splic.system.domain.SysConfig;
import com.splic.system.mapper.SysConfigMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.math.BigDecimal;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.TextStyle;
import java.util.*;

@Service
@Slf4j
public class AccuweatherServiceImpl implements AccuweatherService {

    @Resource
    private ProductService productServiceImpl;

    @Resource
    private UserSubRecordService userSubRecordServiceImpl;

    @Resource
    private WeatherIconService weatherIconServiceImpl;

    @Resource
    private WeatherMapper weatherMapper;

    @Resource
    private WeatherService weatherService;

    @Resource
    private AccuAreaMapper accuAreaMapper;

    @Resource
    private SysConfigMapper configMapper;

    @Value("${accuweather.addressUrl}")
    private String addressUrl;

    @Value("${accuweather.currentWeatherUrl}")
    private String currentWeatherUrl;

    @Value("${accuweather.forecastWeatherUrl}")
    private String forecastWeatherUrl;

    @Value("${accuweather.apikey}")
    private String apikey;

    @Override
    public LocationInfoDTO getAddressByLatLon(String lat, String lon) {
        LocationInfoDTO dto = new LocationInfoDTO();
        try {
            Map<String, Object> paramMap = new HashMap<>();
            paramMap.put("apikey",apikey);
            paramMap.put("q",lat+","+lon);
            paramMap.put("language","zh-cn");
            String result = HttpUtil.get(addressUrl, paramMap, 5000);
            log.info(new Date() + "====" + lat + "====" +lon + "====getAddressByLatLon返回结果result:" + result);
            JSONObject jsonObject = JSONUtil.parseObj(result, true);
            // 国家
            LocationInfoDTO.CountryDTO country = new LocationInfoDTO.CountryDTO();
            country.setId(jsonObject.getJSONObject("Country").getStr("ID"));
            country.setLocalizedName(jsonObject.getJSONObject("Country").getStr("LocalizedName"));
            country.setLocalizedName(jsonObject.getJSONObject("Country").getStr("EnglishName"));
            // 省
            LocationInfoDTO.AdministrativeAreaDTO administrativeArea = new LocationInfoDTO.AdministrativeAreaDTO();
            administrativeArea.setId(jsonObject.getJSONObject("AdministrativeArea").getStr("ID"));
            administrativeArea.setLocalizedName(jsonObject.getJSONObject("AdministrativeArea").getStr("LocalizedName"));
            administrativeArea.setEnglishName(jsonObject.getJSONObject("AdministrativeArea").getStr("EnglishName"));
            administrativeArea.setLevel(jsonObject.getJSONObject("AdministrativeArea").getInt("Level"));
            administrativeArea.setLocalizedType(jsonObject.getJSONObject("AdministrativeArea").getStr("LocalizedType"));
            administrativeArea.setEnglishType(jsonObject.getJSONObject("AdministrativeArea").getStr("EnglishType"));
            administrativeArea.setCountryID(jsonObject.getJSONObject("AdministrativeArea").getStr("CountryID"));
            // 市
            LocationInfoDTO.ParentCityDTO parentCity = new LocationInfoDTO.ParentCityDTO();
            if (jsonObject.get("ParentCity") != null && jsonObject.getJSONObject("ParentCity").get("Key") != null) {
                parentCity.setKey(jsonObject.getJSONObject("ParentCity").getStr("Key"));
                parentCity.setLocalizedName(jsonObject.getJSONObject("ParentCity").getStr("LocalizedName"));
                parentCity.setEnglishName(jsonObject.getJSONObject("ParentCity").getStr("EnglishName"));
            }
            //
            LocationInfoDTO.SupplementalAdminAreaDTO supplementalAdminArea = new LocationInfoDTO.SupplementalAdminAreaDTO();
            if (jsonObject.getJSONArray("SupplementalAdminAreas").size() > 0
                    && StringUtils.isNotEmpty(jsonObject.getJSONArray("SupplementalAdminAreas").getJSONObject(0).getStr("LocalizedName"))) {
                supplementalAdminArea.setLevel(jsonObject.getJSONArray("SupplementalAdminAreas").getJSONObject(0).getInt("Level"));
                supplementalAdminArea.setLocalizedName(jsonObject.getJSONArray("SupplementalAdminAreas").getJSONObject(0).getStr("LocalizedName"));
                supplementalAdminArea.setEnglishName(jsonObject.getJSONArray("SupplementalAdminAreas").getJSONObject(0).getStr("EnglishName"));
            }
            // 地址
            dto.setKey(jsonObject.getStr("Key"));
            dto.setLocalizedName(jsonObject.getStr("LocalizedName"));
            dto.setEnglishName(jsonObject.getStr("EnglishName"));
            dto.setCountry(country);
            dto.setAdministrativeArea(administrativeArea);
            dto.setParentCity(parentCity);
            dto.setSupplementalAdminArea(supplementalAdminArea);
            saveAccArea(dto);
        } catch (Exception e) {
            log.error(new Date() + "====" + lat + "====" +lon + "====getAddressByLatLon异常:" + e.getMessage());
            e.printStackTrace();
        }
        return dto;
    }
    private void saveAccArea(LocationInfoDTO dto) {
        try {
            AccuArea temp = new AccuArea();
            temp.setAreaCode(dto.getKey());
            List<AccuArea> areas = accuAreaMapper.selectAccuAreaList(temp);
            if (areas == null || areas.size() == 0) {
                AccuArea area = new AccuArea();
                area.setAreaCode(dto.getKey());
                area.setAreaName(dto.getLocalizedName());
                area.setLevel(3);
                area.setParentCode(dto.getParentCity().getKey());
                accuAreaMapper.insert(area);
            }
            temp.setAreaCode(dto.getParentCity().getKey());
            areas = accuAreaMapper.selectAccuAreaList(temp);
            if (areas == null || areas.size() == 0) {
                AccuArea area = new AccuArea();
                area.setAreaCode(dto.getParentCity().getKey());
                area.setAreaName(dto.getParentCity().getLocalizedName());
                area.setLevel(2);
                area.setParentCode(dto.getAdministrativeArea().getId());
                accuAreaMapper.insert(area);
            }
            temp.setAreaCode(dto.getAdministrativeArea().getId());
            areas = accuAreaMapper.selectAccuAreaList(temp);
            if (areas == null || areas.size() == 0) {
                AccuArea area = new AccuArea();
                area.setAreaCode(dto.getAdministrativeArea().getId());
                area.setAreaName(dto.getAdministrativeArea().getLocalizedName());
                area.setLevel(1);
                area.setParentCode(dto.getCountry().getId());
                accuAreaMapper.insert(area);
            }
            temp.setAreaCode(dto.getCountry().getId());
            areas = accuAreaMapper.selectAccuAreaList(temp);
            if (areas == null || areas.size() == 0) {
                AccuArea area = new AccuArea();
                area.setAreaCode(dto.getCountry().getId());
                area.setAreaName(dto.getCountry().getLocalizedName());
                area.setLevel(1);
                accuAreaMapper.insert(area);
            }
        } catch (Exception e) {
            log.error(new Date() + "====" + dto + "====saveAccArea异常:" + e.getMessage());
        }
    }
    private String getByLatLon(String lat, String lon) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("apikey",apikey);
        paramMap.put("q",lat+","+lon);
        paramMap.put("language","zh-cn");
        return HttpUtil.get(addressUrl, paramMap, 5000);
    }

    @Override
    public JSONObject getRealtimeWeatherByAreaCode(String areaCode) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("apikey",apikey);
        paramMap.put("details","true");
        paramMap.put("language","zh-cn");
        String result = HttpUtil.get(currentWeatherUrl + areaCode, paramMap, 5000);
        if (result.startsWith("[") && result.endsWith("]")) {
            result = result.substring(1, result.length() - 1);
        }
        log.info(new Date() + "====Accu实时天气返回结果result:" + result);
        return JSONUtil.parseObj(result);
    }

    @Override
    public JSONObject getForecastWeatherByAreaCode(String areaCode) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("apikey",apikey);
        paramMap.put("details","true");
        paramMap.put("language","zh-cn");
        paramMap.put("metric","true");
        String result = HttpUtil.get(forecastWeatherUrl + areaCode, paramMap, 5000);
        log.info(new Date() + "====Accu天气预报返回结果result:" + result);
        return JSONUtil.parseObj(result);
    }

    @Override
    public List<Weather> selectUserWeather(String lat, String lon, Long userId) {
        Product product = productServiceImpl.getByPackageKey(DeviceContext.getProductPackageKey());
        if (product == null){
            throw new BusinessException("应用查询失败。");
        }
        //FIXME 目前只有一个应用对应天气服务  此处goodsTypeId 暂时写死  待后续服务中增加类型之后 在扩展
        Long goodsTypeId = 6L;
        UserSubRecord userSubRecord = userSubRecordServiceImpl.selectUserSubRecord(userId,product.getId(),goodsTypeId);
        if (userSubRecord == null){
            throw new BusinessException("您还没有订阅天气服务");
        }
        if (Objects.equals(0,userSubRecord.getStatus())){
            throw new BusinessException("您的天气服务已过期");
        }

        //根据Accuweather查询定位城市code
        LocationInfoDTO locationInfoDTO = getAddressByLatLon(lat,lon);
        if(locationInfoDTO == null || StringUtils.isEmpty(locationInfoDTO.getKey())){
            throw new BusinessException("坐标查询位置信息失败。");
        }

        List<Weather> weatherList = weatherMapper.selectAccuWeatherByCountyCode(locationInfoDTO.getKey(), DateTimeFormatter.ofPattern("yyyy-MM-dd").format(LocalDateTime.now()));
        try {
            if (CollectionUtils.isEmpty(weatherList) || weatherList.size() < 5){
                // 获取5天天气
                JSONObject forecastJson = getForecastWeatherByAreaCode(locationInfoDTO.getKey());
                if (forecastJson != null) {
                    saveForecastWeather(forecastJson, locationInfoDTO);
                }
            } else {
                boolean flag = false;
                SysConfig config = new SysConfig();
                config.setConfigKey("sys_realtime_refresh");
                SysConfig realtimeConf = configMapper.selectConfig(config);
                config.setConfigKey("sys_forecasts_refresh");
                SysConfig forecastsConf = configMapper.selectConfig(config);
                for(Weather weather:weatherList){
                    if(DateUtils.isSameDay(new Date(),weather.getDate())){
                        //如果当前城市的今日的天气更新时间大于1小时 则重新请求三方获取新的数据
                        if (weather.getUpdateTime().before(DateUtils.addMinutes(new Date(), Integer.parseInt("-" + realtimeConf.getConfigValue())))
                                || StringUtils.isEmpty(weather.getTem())){
                            JSONObject realtimeJson = getRealtimeWeatherByAreaCode(locationInfoDTO.getKey());
                            if (realtimeJson != null) {
                                saveRealtimeWeather(realtimeJson, locationInfoDTO);
                            }
                        }
                    }else{
                        if (!flag) {
                            //如果当前城市的明日的天气更新时间大于6小时 则重新请求三方获取新的数据
                            if (weather.getUpdateTime().before(DateUtils.addHours(new Date(), Integer.parseInt("-" + forecastsConf.getConfigValue())))){
                                JSONObject forecastJson = getForecastWeatherByAreaCode(locationInfoDTO.getKey());
                                if (forecastJson != null) {
                                    saveForecastWeather(forecastJson, locationInfoDTO);
                                    flag = true;
                                }
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("获取天气信息失败",e);
        }
        List<Weather> resultWeatherList = weatherMapper.selectAccuWeatherByCountyCode(locationInfoDTO.getKey(), DateUtils.dateTime(DateUtils.getCurrentDayStartTime()));
        //替换天气图标
        if(CollectionUtils.isNotEmpty(resultWeatherList)){
            for (Weather weather1 :resultWeatherList){
                WeatherIcon icon = weatherIconServiceImpl.getByName(weather1.getWeaImg());
                if(icon != null){
                    weather1.setWeaImg(icon.getIcon());
                }
            }
        }
        try {
            log.info("====\n" + DeviceContext.getDeviceIdentify() + "====\n" + product.getName() +"====\n" + new Date() + "====\n" + lat + "====\n" + lon + "====\n" + locationInfoDTO.getKey() + "====\n" + resultWeatherList.get(0) + "====\n" + resultWeatherList.get(1));
        } catch (Exception e) {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            e.printStackTrace(new PrintStream(baos));
            String exception = baos.toString();
            log.error("baos:" + exception);
        }
        return resultWeatherList;
    }

    private void saveForecastWeather(JSONObject jsonObject, LocationInfoDTO locationInfoDTO) {
        JSONArray dailyForecasts = jsonObject.getJSONArray("DailyForecasts");
        JSONObject temp;
        for (Object forecast : dailyForecasts) {
            temp = (JSONObject) forecast;
            LocalDate date = getLocalDate(temp.getStr("Date"));
            if (date.isBefore(LocalDate.now())) {
                continue;
            }
            Weather weatherOld = weatherMapper.selectAccuWeather(locationInfoDTO.getKey(),date.toString());
            Weather weather = new Weather();
            Object maxO = temp.getJSONObject("Temperature").getJSONObject("Maximum").get("Value");
            int max = convertObj2Int(maxO);
            if (max != 1000) {
                weather.setTem1(String.valueOf(max));
            }
            Object minO = temp.getJSONObject("Temperature").getJSONObject("Minimum").get("Value");
            int min = convertObj2Int(minO);
            if (min != 1000) {
                weather.setTem2(String.valueOf(min));
            }
            weather.setWeaImg(String.valueOf(temp.getJSONObject("Day").getInt("Icon")));
            weather.setWea(temp.getJSONObject("Day").getStr("IconPhrase"));
            weather.setWin(temp.getJSONObject("Day").getJSONObject("Wind").getJSONObject("Direction").getStr("Localized"));
            weather.setWinSpeed(convertWindSpeedToPower(temp.getJSONObject("Day").getJSONObject("Wind").getJSONObject("Speed").getDouble("Value").intValue()));
            if(weatherOld != null){
                weather.setId(weatherOld.getId());
                if (date.isEqual(LocalDate.now())) {
                    weather.setWeaImg(null);
                    weather.setWea(null);
                }
            } else {
                weather.setCountyCode(locationInfoDTO.getKey());
                if (locationInfoDTO.getParentCity() != null && locationInfoDTO.getParentCity().getKey() != null) {
                    weather.setCityCode(locationInfoDTO.getParentCity().getKey());
                }
                weather.setProvinceCode(locationInfoDTO.getAdministrativeArea().getId());
                weather.setCountryCode(locationInfoDTO.getCountry().getId());
                weather.setDate(Date.from(date.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
                weather.setWeek(getWeekName(date));
            }
            weatherService.saveOrUpdate(weather);
        }
        // 获取实时天气
        JSONObject realtimeJson = getRealtimeWeatherByAreaCode(locationInfoDTO.getKey());
        if (realtimeJson != null) {
            saveRealtimeWeather(realtimeJson, locationInfoDTO);
        }
    }

    private void saveRealtimeWeather(JSONObject jsonObject, LocationInfoDTO locationInfoDTO) {
        Weather weatherOld = weatherMapper.selectAccuWeather(locationInfoDTO.getKey(),LocalDate.now().toString());
        Weather weather = new Weather();
        weather.setWea(jsonObject.getStr("WeatherText"));
        weather.setWeaImg(jsonObject.getStr("WeatherIcon"));
        Object realO = jsonObject.getJSONObject("Temperature").getJSONObject("Metric").get("Value");
        int real = convertObj2Int(realO);
        if (real != 1000) {
            weather.setTem(String.valueOf(real));
        }
        if (weatherOld == null) {
            weather.setCountyCode(locationInfoDTO.getKey());
            if (locationInfoDTO.getParentCity() != null && locationInfoDTO.getParentCity().getKey() != null) {
                weather.setCityCode(locationInfoDTO.getParentCity().getKey());
            }
            weather.setProvinceCode(locationInfoDTO.getAdministrativeArea().getId());
            weather.setCountryCode(locationInfoDTO.getCountry().getId());
            weather.setDate(Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant()));
            weather.setWeek(getWeekName(LocalDate.now()));
        } else {
            weather.setId(weatherOld.getId());
        }
        weather.setWin(jsonObject.getJSONObject("Wind").getJSONObject("Direction").getStr("Localized"));
        weather.setWinSpeed(convertWindSpeedToPower(jsonObject.getJSONObject("Wind").getJSONObject("Speed").getJSONObject("Metric").getDouble("Value").intValue()));
        weatherService.saveOrUpdate(weather);
    }
    public int convertObj2Int(Object obj) {
        int result;
        if (obj instanceof String) {
            try {
                result = Integer.parseInt((String) obj);
            } catch (NumberFormatException e) {
                result = 1000;
            }
        } else if (obj instanceof Integer) {
            result = (Integer) obj;
        } else if (obj instanceof Double) {
            result = ((Double) obj).intValue();
        } else if (obj instanceof BigDecimal) {
            result = ((BigDecimal) obj).intValue();
        } else {
            result = 1000;
        }
        return result;
    }

    public String convertWindSpeedToPower(int windSpeedKmh) {
        // 风速与风力等级的分界值
        int[] windSpeedBoundaries = {1, 5, 11, 19, 28, 38, 49, 61, 74, 88, 102, 117};

        for (int i = 0; i < windSpeedBoundaries.length; i++) {
            if (windSpeedKmh <= windSpeedBoundaries[i]) {
                return i + "级";
            }
        }

        // 如果风速超过最大已知风力等级，返回 12 表示台风等级
        return 12 + "级";
    }

    public String getWeekName(LocalDate localDate) {
        // 获取星期几的 DayOfWeek 枚举值
        DayOfWeek dayOfWeek = localDate.getDayOfWeek();
        // 设置显示样式和本地化信息
        TextStyle style = TextStyle.FULL; // 或者 TextStyle.SHORT, TextStyle.NARROW 等
        Locale locale = Locale.CHINESE; // 或者 Locale.CHINA, Locale.US 等
        return dayOfWeek.getDisplayName(style, locale);
    }

    public LocalDate getLocalDate(String inputDateString) {
        // 创建一个符合 ISO-8601 格式的 DateTimeFormatter
        DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;

        // 使用 formatter 解析输入字符串为 OffsetDateTime
        OffsetDateTime offsetDateTime = OffsetDateTime.parse(inputDateString, formatter);

        // 从 OffsetDateTime 中提取 LocalDate
        return offsetDateTime.toLocalDate();
    }
}
